﻿using LEDScreenLinkTool.Model;
using LEDScreenLinkTool.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace LEDScreenLinkTool.ViewModel
{
    public class NovaConnectViewModel : NotifyPropertyBase
    {
        #region 属性

        private ScreenGridModel gridModel;
        private ConnectLineDrawing linkLine;
        private List<SolutionLine> solutions;
        private int curScreenId;
        private int totalPixels;
        #endregion

        #region 数据源

        private ObservableCollection<CmbBindingModel> _itModuleList;
        /// <summary>
        /// 右侧统计各灯板使用数
        /// </summary>
        public ObservableCollection<CmbBindingModel> ITModule
        {
            get { return _itModuleList; }
            set
            {
                _itModuleList = value;
                OnPropertyChanged();
            }
        }

        private string _title;
        public string WinTitle
        {
            get { return _title; }
            set
            {
                _title = value;
                OnPropertyChanged();
            }
        }

        private int bodyWidth;

        private int bodyHeight;
        /// <summary>
        /// 模块宽度
        /// </summary>
        private int moduleWidth;
        /// <summary>
        /// 模块高度
        /// </summary>
        private int moduleHeight;
        /// <summary>
        /// 整屏宽度
        /// </summary>
        public string BodyWidth
        {
            get { return bodyWidth >= 0 ? bodyWidth.ToString() : ""; }
            set
            {
                if (int.TryParse(value, out bodyWidth))
                {
                    OnPropertyChanged();
                }
            }
        }
        /// <summary>
        /// 整屏高度
        /// </summary>
        public string BodyHeight
        {
            get { return bodyHeight >= 0 ? bodyHeight.ToString() : ""; }
            set
            {
                if (int.TryParse(value, out bodyHeight))
                {
                    OnPropertyChanged();
                }
            }
        }

        public bool IsLoading;

        private int _scanNum;
        public int ScanNum
        {
            get { return _scanNum; }
            set
            {
                _scanNum = value;
                OnPropertyChanged();
            }
        }



        private ObservableCollection<ScreenConfigItem> _moduleList;

        /// <summary>
        /// 灯板数据类集合
        /// </summary>
        public ObservableCollection<ScreenConfigItem> ModuleList
        {
            get { return _moduleList; }
            set
            {
                _moduleList = value;
                OnPropertyChanged();
            }
        }

        private int _screenWidth;
        public int ScreenWidth
        {
            get { return _screenWidth; }
            set
            {
                _screenWidth = value;
                OnPropertyChanged();
            }
        }

        private int _screenHeight;
        public int ScreenHeight
        {
            get { return _screenHeight; }
            set
            {
                _screenHeight = value;
                OnPropertyChanged();
            }
        }



        private string _tipsStr;

        public string TipsText
        {
            get { return _tipsStr; }
            set
            {
                _tipsStr = value;
                OnPropertyChanged();
            }
        }

        private ObservableCollection<ModuleGridItem> _ledScreen;
        /// <summary>
        /// 灯板图形类集合,用于响应界面交互
        /// </summary>
        public ObservableCollection<ModuleGridItem> LedScreen
        {
            get { return _ledScreen; }
            set
            {
                _ledScreen = value;
                OnPropertyChanged();
            }
        }



        private ObservableCollection<CmbBindingModel> _moduleSizeList;
        public ObservableCollection<CmbBindingModel> ModuleSizeList
        {
            get { return _moduleSizeList; }
            set
            {
                _moduleSizeList = value;
                OnPropertyChanged();
            }
        }
        private CmbBindingModel _moduleSizeSelectedItem;
        public CmbBindingModel ModuleSizeSelectedItem
        {
            get { return _moduleSizeSelectedItem; }
            set
            {
                _moduleSizeSelectedItem = value;
                OnPropertyChanged();
            }
        }



        private ObservableCollection<CmbBindingModel> _solutionList;
        public ObservableCollection<CmbBindingModel> SolutionList
        {
            get { return _solutionList; }
            set
            {
                _solutionList = value;
                OnPropertyChanged();
            }
        }

        private int _solutionItem;
        public int SolutionNo
        {
            get { return _solutionItem; }
            set
            {
                _solutionItem = value;
                OnPropertyChanged();
            }
        }

        public string SolutionTotal
        {
            get { return $"/{_solutionList.Count}"; }
        }
        #endregion

        public NovaConnectViewModel()
        {
            string title = ResourceHelper.GetString("Key_Title");
            WinTitle = title;
            SolutionList = new ObservableCollection<CmbBindingModel>();
            ITModule = new ObservableCollection<CmbBindingModel>();
            ModuleList = new ObservableCollection<ScreenConfigItem>();
            LedScreen = new ObservableCollection<ModuleGridItem>();
            gridModel = new ScreenGridModel();
            linkLine = new ConnectLineDrawing();
            solutions = new List<SolutionLine>();
            InitData();
        }

        #region 共有方法


        public void Dispose()
        {

        }

        /// <summary>
        /// 屏体使用图片显示,减少wpf绘制消耗,每次绘制新的图片路径替换,确保旧文件不被占用释放不到.
        /// </summary>
        /// <param name="pictureName"></param>
        public void RemakeModules(out string pictureName)
        {
            pictureName = string.Empty;
            LedScreen.Clear();
            double totalWidth = ScreenWidth - 11;
            double totalHeight = ScreenHeight - 11;
            if (ModuleList.Count > 0 && (bodyWidth > 0 && bodyHeight > 0))
            {
                double widthRate = totalWidth / (bodyWidth * 1d);
                double heightRate = totalHeight / (bodyHeight * 1d);
                var ScreenModuleList = ModuleList.OrderBy(x => x.Y).ThenBy(x => x.X).ToList();
                foreach (var module in ScreenModuleList)
                {
                    ModuleGridItem gridItem = new ModuleGridItem();
                    gridItem.ScreenId = module.ScreenId;
                    gridItem.GridWidth = module.Width * widthRate;
                    gridItem.GridHeight = module.Height * heightRate;
                    gridItem.ModuleType = module.ModuleType;
                    if (module.X > 0)
                    {
                        var leftModule = ModuleList.Where(x => x.Y == module.Y && x.X < module.X).OrderByDescending(x => x.X).FirstOrDefault();
                        if (leftModule != null)
                        {
                            int leftId = leftModule.ScreenId;
                            var leftSibling = LedScreen.FirstOrDefault(s => s.ScreenId == leftId);
                            gridItem.GridLeft = leftSibling.GridLeft + leftSibling.GridWidth;
                        }
                    }
                    if (module.Y > 0)
                    {
                        var topModule = ModuleList.Where(x => x.Y < module.Y && x.X <= module.X).OrderByDescending(x => x.Y).ThenByDescending(x => x.X).FirstOrDefault();
                        if (topModule != null)
                        {
                            int topId = topModule.ScreenId;
                            var topSibling = LedScreen.FirstOrDefault(s => s.ScreenId == topId);
                            gridItem.GridTop = topSibling.GridTop + topSibling.GridHeight;
                        }
                    }
                    LedScreen.Add(gridItem);
                }
            }
            else
            {
                //生成一张空白的预览图
                ModuleGridItem emptyItem = new ModuleGridItem();
                emptyItem.GridWidth = totalWidth;
                emptyItem.GridHeight = totalHeight;
                LedScreen.Add(emptyItem);
            }

            string pngName = Guid.NewGuid().ToString("N") + ".png";
            string pictPath=Path.Combine(AppContext.BaseDirectory,"Screens", pngName);
            SaveScreenPictureOper saveOper = new SaveScreenPictureOper();
            var saveRst= saveOper.SavePicture(totalWidth, totalHeight, LedScreen.ToList(), pictPath);
            if(saveRst)
            {
                pictureName = pngName;
            }
        }

        public void GetConnectSolutions()
        {
            if (solutions != null)
            {
                for (int i = 0; i < solutions.Count; i++)
                {
                    var item=solutions[i];
                    item.Points.Clear();
                    for (int j = 0; j < item.GridNos.Count; j++)
                    {
                        int idx = item.GridNos[j];
                        var module = LedScreen.FirstOrDefault(m => m.ScreenId == idx);
                        if (module != null)
                        {
                            item.Points.Add(new Point(module.GridLeft + module.GridWidth * 0.5, module.GridTop + module.GridHeight * 0.5));
                        }
                    }
                }
            }
        }

        public void RefreshLine()
        {
            if (SolutionNo >= 0)
            {
                int index = SolutionNo - 1;
                linkLine.RefreshLine(index);
            }
        }
        public void PreLine()
        {
            if (SolutionNo > 1)
            {
                SolutionNo = SolutionNo - 1;
                int index = SolutionNo - 1;
                linkLine.RefreshLine(index);
            }
        }

        public void NextLine()
        {
            if (SolutionNo < _solutionList.Count)
            {
                SolutionNo = SolutionNo + 1;
                int index = SolutionNo - 1;
                linkLine.RefreshLine(index);
            }
        }

        public void ChangeDisplayLink(Canvas mainCv)
        {
            if (!mainCv.Children.Contains(linkLine))
            {
                mainCv.Children.Add(linkLine);
            }
            RefreshLine();
        }

        public void ModuleSizeChange()
        {
            if (ModuleSizeSelectedItem != null)
            {
                var moduleSize = (ModuleSize)ModuleSizeSelectedItem.OptionValue;
                moduleWidth = moduleSize.Width;
                moduleHeight = moduleSize.Height;
            }
        }




        public EErrorCode ResizeScreenBody(out string pictureName)
        {
            EErrorCode rst = EErrorCode.Failure;
            pictureName = string.Empty;
            linkLine.ClearPoints();
            SolutionList = new ObservableCollection<CmbBindingModel>();
            SolutionNo = -1;
            curScreenId = -1;
            try
            {
                rst = ResizeModules();
                if (rst == EErrorCode.Success)
                {
                    RemakeModules(out pictureName);
                    CaculateModuleConnectLines();
                    if (SolutionList.Count > 0)
                    {
                        GetConnectSolutions();
                        SolutionNo = 1;
                        linkLine.RefreshLine(0);
                    }
                }
            }
            catch(Exception ex)
            {
                LogHelper.Error("<ResizeScreenBody>", ex);
            }
            return rst;
        }

        public bool DrawStartLine(double posX, double posY)
        {
            bool callRst = false;
            var selectScreen = LedScreen.FirstOrDefault(t => t.GridLeft <= posX && t.GridTop <= posY
                                         && (t.GridLeft + t.GridWidth) >= posX
                                         && (t.GridTop + t.GridHeight) >= posY);
            if (selectScreen != null)
            {
                if (selectScreen.ScreenId != curScreenId)
                {
                    Stopwatch sw=new Stopwatch();
                    sw.Start();
                    try
                    {
                        callRst = CaculateModuleConnectLines(selectScreen.ScreenId);
                        if (SolutionList.Count > 0)
                        {
                            GetConnectSolutions();
                            SolutionNo = 1;
                            linkLine.RefreshLine(0);
                        }
                        curScreenId = selectScreen.ScreenId;
                    }
                    catch(Exception ex)
                    {
                        LogHelper.Error("<DrawStartLine>", ex);
                    }
                    sw.Stop();
                    if(sw.ElapsedMilliseconds<500)
                    {
                        Thread.Sleep((int)(500- sw.ElapsedMilliseconds));
                    }
                }
                else
                {
                    callRst = true;
                }
            }
            return callRst;
        }

        #endregion

        #region 私有方法

        private void InitData()
        {
            var sizeList = AppInfoInstance.GetModuleList();
            _moduleSizeList = new ObservableCollection<CmbBindingModel>();
            foreach (var item in sizeList)
            {
                _moduleSizeList.Add(new CmbBindingModel()
                {
                    DisplayName = item.Name,
                    OptionValue = item
                });
            }
            if (sizeList.Count > 0)
            {
                ModuleSizeSelectedItem = _moduleSizeList[0];
                moduleWidth = sizeList[0].Width;
                moduleHeight = sizeList[0].Height;
            }
            OnPropertyChanged("ModuleSizeList");

            totalPixels = 0;
            curScreenId = -1;

        }

        /// <summary>
        /// 根据整屏大小,计算各种灯板用量和排列
        /// 优先用2*2 填充,宽为奇数则最右为2*1填充
        /// 高为奇数则最下3行用3*1填充
        /// <br/>灯板编号 从列由左至右,继而行由上至下,编号由0开始
        /// </summary>
        /// <returns></returns>
        private EErrorCode ResizeModules()
        {
            EErrorCode rtnCode = EErrorCode.Failure;
            int mHeight = moduleHeight;
            int mWidth = moduleWidth;
            totalPixels = 0;
            if ((bodyHeight % mHeight) > 0 || (bodyWidth % mWidth) > 0)
            {
                rtnCode = EErrorCode.SizeError;
                goto END;
            }
            totalPixels = bodyHeight * bodyWidth;
            if (totalPixels > ParamStruct.KMaxPixels)
            {
                rtnCode = EErrorCode.SizeError;
                totalPixels = 0;
                goto END;
            }
            int modCountX = (int)(bodyWidth / mWidth);
            int modCountY = (int)(bodyHeight / mHeight);
            int rowCount = modCountY / 2;
            int colCount = modCountX / 2;
            if (modCountY % 2 == 1)
            {
                rowCount -= 1;
            }
            bool hasSide = false;
            if (modCountX % 2 == 1)
            {
                hasSide = true;
                colCount += 1;
            }
            ModuleList.Clear();
            ScreenConfigItem cfItem;
            for (int j = 0; j < rowCount; j++)
            {
                for (int i = 0; i < colCount; i++)
                {
                    cfItem = new ScreenConfigItem();
                    cfItem.ScreenId = colCount * j + i;
                    cfItem.X = i * 2 * mWidth;
                    cfItem.Y = j * 2 * mHeight;
                    if (hasSide && i == (colCount - 1))
                    {
                        cfItem.Width = 1 * mWidth;
                        cfItem.Height = 2 * mHeight;
                        cfItem.ModuleType = EModuleType.P4_2x1;
                        cfItem.TotalPixel = (int)(moduleHeight * moduleWidth * 2);
                    }
                    else
                    {
                        cfItem.Width = 2 * mWidth;
                        cfItem.Height = 2 * mHeight;
                        cfItem.ModuleType = EModuleType.P4_2x2;
                        cfItem.TotalPixel = (int)(moduleHeight * moduleWidth * 4);
                    }
                    //cfItem.RcfgxPath = CopyConfigFile(cfItem.ScreenId, ModuleSizeSelectedItem?.DisplayName, cfItem.ModuleType);
                    ModuleList.Add(cfItem);
                }
            }
            int realRowCount = rowCount;
            int nextIndex = rowCount * colCount;
            if (modCountY % 2 == 1)
            {
                realRowCount += 1;
                int j = rowCount;
                for (int i = 0; i < modCountX; i++)
                {
                    cfItem = new ScreenConfigItem();
                    cfItem.ScreenId = nextIndex++;
                    cfItem.X = i * mWidth;
                    cfItem.Y = j * 2 * mHeight;
                    cfItem.Width = 1 * mWidth;
                    cfItem.Height = 3 * mHeight;
                    cfItem.ModuleType = EModuleType.P4_3x1;
                    cfItem.TotalPixel = (int)(moduleHeight * moduleWidth * 3);
                    //cfItem.RcfgxPath = CopyConfigFile(cfItem.ScreenId, ModuleSizeSelectedItem?.DisplayName, cfItem.ModuleType);
                    ModuleList.Add(cfItem);
                }
            }
            //限制灯板数量,以免计算耗时过多
            if (ModuleList.Count > ParamStruct.KMaxModuleCount)
            {
                rtnCode = EErrorCode.CaculateFail;
                totalPixels = 0;
                ModuleList.Clear();
                goto END;
            }
            ScanNum = ModuleList.Count;
            var sizeResult = SetScreenSize(modCountX, modCountY);
            if(!sizeResult)
            {
                rtnCode = EErrorCode.CaculateFail;
                totalPixels = 0;
                ModuleList.Clear();
                goto END;
            }
            gridModel = new ScreenGridModel(ModuleList.ToList(), colCount, realRowCount);
            rtnCode = EErrorCode.Success;
            END:
            return rtnCode;
        }

        private bool CaculateModuleConnectLines(int screenId)
        {
            bool hasSolution = false;
            if (ModuleList.Count > 0)
            {
                var findRst = gridModel.FindConnectLines(screenId);
                if (findRst.Count > 0)
                {
                    solutions = findRst;
                    linkLine.SetLinkLine(findRst);
                    _solutionList.Clear();
                    for (int i = 0; i < solutions.Count; i++)
                    {
                        _solutionList.Add(new CmbBindingModel()
                        {
                            DisplayName = (i + 1).ToString(),
                            OptionValue = (i + 1)
                        });
                    }
                    hasSolution = true;
                    OnPropertyChanged("SolutionTotal");
                    OnPropertyChanged("SolutionList");
                }
                else
                {
                    linkLine.ClearPoints();
                    SolutionList = new ObservableCollection<CmbBindingModel>();
                    SolutionNo = -1;
                }
            }
            return hasSolution;
        }

        private bool CaculateModuleConnectLines()
        {
            bool hasSolution = false;
            if (ModuleList.Count > 0)
            {
                var findRst = gridModel.FindCornersConnectLine();
                if (findRst.Count > 0)
                {
                    solutions = findRst;
                    linkLine.SetLinkLine(findRst);
                    _solutionList.Clear();
                    for (int i = 0; i < solutions.Count; i++)
                    {
                        _solutionList.Add(new CmbBindingModel()
                        {
                            DisplayName = (i + 1).ToString(),
                            OptionValue = (i + 1)
                        });
                    }
                    hasSolution = true;
                    OnPropertyChanged("SolutionTotal");
                    OnPropertyChanged("SolutionList");
                }
                else
                {
                    linkLine.ClearPoints();
                    SolutionList = new ObservableCollection<CmbBindingModel>();
                    SolutionNo = -1;
                }
            }
            return hasSolution;
        }

        private bool SetScreenSize(int moduleCountX = 0, int moduleCountY = 0)
        {
            var sumData = (from module in ModuleList
                           group module by module.ModuleType
                           into temp
                           select new
                           {
                               mtype = temp.Key,
                               mcount = temp.Count()
                           }).ToList();

            ITModule.Clear();
            bool noError = true;
            foreach (var modItem in sumData)
            {
                if (modItem.mtype == EModuleType.None || modItem.mtype == EModuleType.Unknown)
                {
                    noError = false;
                }
                ITModule.Add(new CmbBindingModel()
                {
                    DisplayName = string.Format("{0}：{1}", ResourceHelper.GetString($"EnumModule_{(int)modItem.mtype}"), modItem.mcount),
                    OptionValue = modItem.mtype
                });
            }
            IsLoading = true;
            if (noError)
            {
            }
            TOEND:
            IsLoading = false;
            return noError;
        }

        private bool RelocateModules(int moduleCountX, int moduleCountY)
        {
            bool caculateRst = true;
            var m3List= ModuleList.Where(m => m.ModuleType == EModuleType.P4_3x1).OrderBy(m => m.ScreenId).ToList();
            var m21List = ModuleList.Where(m => (m.ModuleType == EModuleType.P4_2x1)).OrderBy(m => m.ScreenId).ToList();
            var m22List = ModuleList.Where(m => m.ModuleType == EModuleType.P4_2x2).OrderBy(m => m.ScreenId).ToList();
            int rowCount = (int)Math.Floor(moduleCountY / 2d);
            int colCount = (int)Math.Floor(moduleCountX / 2d);
            int m22Index = 0, m21Index = 0, m3Index = 0;
            if (moduleCountY % 2 == 1)
            {
                rowCount -= 1;
            }
            bool hasSide = false;
            if (moduleCountX % 2 == 1)
            {
                hasSide = true;
                colCount += 1;
            }
            try
            {
                ScreenConfigItem cfItem;
                for (int j = 0; j < rowCount; j++)
                {
                    for (int i = 0; i < colCount; i++)
                    {
                        if (hasSide && i == (colCount - 1))
                        {
                            if (m21Index < m21List.Count)
                            {
                                cfItem = m21List[m21Index];
                                cfItem.ScreenId = colCount * j + i;
                                cfItem.X = i * 2 * moduleWidth;
                                cfItem.Y = j * 2 * moduleHeight;
                                cfItem.Width = 1 * moduleWidth;
                                cfItem.Height = 2 * moduleHeight;
                                m21Index++;
                            }
                        }
                        else
                        {
                            if (m22Index < m22List.Count)
                            {
                                cfItem = m22List[m22Index];
                                cfItem.ScreenId = colCount * j + i;
                                cfItem.X = i * 2 * moduleWidth;
                                cfItem.Y = j * 2 * moduleHeight;
                                cfItem.Width = 2 * moduleWidth;
                                cfItem.Height = 2 * moduleHeight;
                                m22Index++;
                            }
                        }
                    }
                }
                int nextIndex = rowCount * colCount;
                if (moduleCountY % 2 == 1)
                {
                    int j = rowCount;
                    for (int i = 0; i < moduleCountX; i++)
                    {
                        cfItem = null;
                        if (m3Index < m3List.Count)
                        {
                            cfItem = m3List[m3Index];
                            m3Index++;
                        }
                        if (cfItem != null)
                        {
                            cfItem.ScreenId = nextIndex++;
                            cfItem.X = i * moduleWidth;
                            cfItem.Y = j * 2 * moduleHeight;
                            cfItem.Width = 1 * moduleWidth;
                            cfItem.Height = 3 * moduleHeight;
                        }
                    }
                }
                caculateRst = true;
            }
            catch(Exception ex)
            {
                caculateRst = false;
            }
            return caculateRst;
        }


        #endregion
    }
}
